#include <tchar.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "GoogleService.hpp"
#include "GoogleContact.hpp"

#include "TinyXML/tinyxml.h"

static TiXmlElement* ToElement(TiXmlNode* pNode);
static const char* ToText(TiXmlNode* pNode);
static const char* ToText(TiXmlElement* pElement);
static tstring EntryGetETag(TiXmlElement* pElement);
static tstring EntryGetFullname(TiXmlElement* pElement);
static tstring EntryGetFirst(TiXmlElement* pElement);
static tstring EntryGetLast(TiXmlElement* pElement);
static tstring EntryGetOrganization(TiXmlElement* pElement);
static tstring EntryGetNote(TiXmlElement* pElement);
static tstring EntryGetBirthDay(TiXmlElement* pElement);
static tstring EntryGetPhoneNumber(TiXmlElement* pElement);
static tstring EntryGetPhoneLabel(TiXmlElement* pElement);
static bool ParseXML(const char* pText, ContactData& Data);

bool Person::AddPhone(tstring number, tstring label)
{
	if(_phones.find(number)!=_phones.end()) return false;
	Phone p;
	p._number = number;
	p._label = label;
	_phones[number] = p;
	return true;
}

bool Person::DelPhone(tstring number)
{
	std::map<tstring, Phone>::iterator i;
	i = _phones.find(number);
	if(i==_phones.end()) return false;
	_phones.erase(i);
	return true;
}

bool Person::HasPhone(tstring number)
{
	return _phones.find(number)!=_phones.end();
}

bool ContactData::AddPerson(tstring fullname)
{
	if(_persons.find(fullname)!=_persons.end()) return false;
	Person p;
	p._fullname = fullname;
	_persons[fullname] = p;
	return true;
}

bool ContactData::DelPerson(tstring fullname)
{
	std::map<tstring, Person>::iterator i;
	i = _persons.find(fullname);
	if(i==_persons.end()) return false;
	_persons.erase(i);
	return true;
}

bool ContactData::HasPerson(tstring fullname)
{
	return _persons.find(fullname)!=_persons.end();
}

Person& ContactData::GetPerson(tstring fullname)
{
	return _persons[fullname];
}

CGoogleContact::CGoogleContact(LPCTSTR pSource) : CGoogleService(pSource, _T("cp"))
{
}

bool CGoogleContact::GetContacts(ContactData& Data)
{
	DWORD dwDataSize = 500*1024;
	LPSTR pData = (LPSTR)malloc(dwDataSize);

	if(!HttpGet(_T("/m8/feeds/contacts/default/full?max-results=1000"), pData, dwDataSize)) return false;
	if(!ParseXML(pData, Data)) return false;

	return true;
}

TiXmlElement* ToElement(TiXmlNode* pNode)
{
	if(pNode==NULL) return NULL;
	return pNode->ToElement();
}

const char* ToText(TiXmlNode* pNode)
{
	return ToText(ToElement(pNode));
}

const char* ToText(TiXmlElement* pElement)
{
	if(pElement==NULL) return NULL;
	return pElement->GetText();
}

tstring EntryGetETag(TiXmlElement* pElement)
{
	const char* etag = pElement->Attribute("gd:etag");
	if(etag==NULL) return _T("");
	return Utf8ToTCHAR(etag);
}

tstring EntryGetFullname(TiXmlElement* pElement)
{
	TiXmlElement* pName = ToElement(pElement->FirstChild("gd:name"));
	if(pName) {
		const char* pValue = ToText(pName->FirstChild("gd:fullName"));
		if(pValue) {
			return Utf8ToTCHAR(pValue);
		}
	}

	const char* pValue = ToText(pElement->FirstChild("Title"));
	if(!pValue) return _T("");
	return Utf8ToTCHAR(pValue);
}

tstring EntryGetFirst(TiXmlElement* pElement)
{
	TiXmlElement* pName = ToElement(pElement->FirstChild("gd:name"));
	if(pName) {
		const char* pValue = ToText(pName->FirstChild("gd:first"));
		if(pValue) {
			return Utf8ToTCHAR(pValue);
		}
	}
	return _T("");
}

tstring EntryGetLast(TiXmlElement* pElement)
{
	TiXmlElement* pName = ToElement(pElement->FirstChild("gd:name"));
	if(pName) {
		const char* pValue = ToText(pName->FirstChild("gd:last"));
		if(pValue) {
			return Utf8ToTCHAR(pValue);
		}
	}
	return _T("");
}

tstring EntryGetOrganization(TiXmlElement* pElement)
{
	TiXmlElement* pName = ToElement(pElement->FirstChild("gd:organization"));
	if(pName) {
		const char* pValue = ToText(pName->FirstChild("gd:orgName"));
		if(pValue) {
			return Utf8ToTCHAR(pValue);
		}
	}
	return _T("");
}

tstring EntryGetNote(TiXmlElement* pElement)
{
	const char* pValue = ToText(pElement->FirstChild("content"));
	if(pValue) {
		return Utf8ToTCHAR(pValue);
	}
	return _T("");
}

tstring EntryGetBirthDay(TiXmlElement* pElement)
{
	TiXmlElement* pValue = ToElement(pElement->FirstChild("gContact:birthday"));
	if(pValue) {
		const char* pWhen = pValue->Attribute("when");
		if(pWhen) {
			return Utf8ToTCHAR(pWhen);
		}
	}
	return _T("");
}

tstring EntryGetPhoneNumber(TiXmlElement* pElement)
{
	const char* pValue = ToText(pElement);
	if(pValue) {
		return Utf8ToTCHAR(pValue);
	}
	return _T("");
}

tstring EntryGetPhoneLabel(TiXmlElement* pElement)
{
	const char* pLabel = pElement->Attribute("rel");
	if(pLabel) {
		pLabel = strrchr(pLabel, '#');
		if(pLabel) {
			return Utf8ToTCHAR(pLabel+1);
		}
	}
	return _T("");
}

bool ParseXML(const char* pText, ContactData& Data)
{
	TiXmlDocument doc;
	doc.Parse(pText);
	if(doc.Error()) return false;

	const char* pTotal = ToText(ToElement(doc.RootElement()->FirstChild("openSearch:totalResults")));
	const char* pCount = ToText(ToElement(doc.RootElement()->FirstChild("openSearch:itemsPerPage")));
	if(pTotal==NULL || pCount==NULL) return false;

	Data._persons.clear();
	TiXmlNode* pEntryNode = doc.RootElement()->IterateChildren("entry", NULL);
	for(; pEntryNode; pEntryNode = doc.RootElement()->IterateChildren("entry", pEntryNode)) {
		TiXmlElement* pEntry = ToElement(pEntryNode);
		if(!pEntry) continue;

		tstring etag			= EntryGetETag(pEntry);
		tstring fullname		= EntryGetFullname(pEntry);
		if(etag.empty() || fullname.empty()) continue;

		if(!Data.AddPerson(fullname)) continue;

		Data.GetPerson(fullname)._etag			= etag;
		Data.GetPerson(fullname)._frist			= EntryGetFirst(pEntry);
		Data.GetPerson(fullname)._last			= EntryGetLast(pEntry);
		Data.GetPerson(fullname)._organization	= EntryGetOrganization(pEntry);
		Data.GetPerson(fullname)._note			= EntryGetNote(pEntry);
		Data.GetPerson(fullname)._birthday		= EntryGetBirthDay(pEntry);

		TiXmlNode* pItemNode = pEntry->FirstChild("gd:phoneNumber");
		for(; pItemNode; pItemNode = pEntry->IterateChildren("gd:phoneNumber", pItemNode)) {
			tstring number, label;
			number = EntryGetPhoneNumber(ToElement(pItemNode));
			if(number.empty()) continue;
			label = EntryGetPhoneLabel(ToElement(pItemNode));
			Data.GetPerson(fullname).AddPhone(number, label);
		}
	}

	return true;
}
